Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.89% covered (success)
91.89%
68 / 74
37.50% covered (danger)
37.50%
3 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
MetadataFactory
91.89% covered (success)
91.89%
68 / 74
37.50% covered (danger)
37.50%
3 / 8
41.90
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMetadataStrategy
96.00% covered (success)
96.00%
24 / 25
0.00% covered (danger)
0.00%
0 / 1
13
 getScalarForType
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 getMetadataStrategyForType
93.33% covered (success)
93.33%
28 / 30
0.00% covered (danger)
0.00%
0 / 1
17.09
 getMethodMetadata
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getCreationMetadata
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getModificationMetadata
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getResultMetadata
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2namespace Apie\Core\Metadata;
3
4use Apie\Core\Context\ApieContext;
5use Apie\Core\Context\MetadataFieldHashmap;
6use Apie\Core\Enums\ScalarType;
7use Apie\Core\Exceptions\InvalidTypeException;
8use Apie\Core\Metadata\Fields\ConstructorParameter;
9use Apie\Core\Metadata\Strategy\BuiltInPhpClassStrategy;
10use Apie\Core\Metadata\Strategy\CompositeValueObjectStrategy;
11use Apie\Core\Metadata\Strategy\DtoStrategy;
12use Apie\Core\Metadata\Strategy\EnumStrategy;
13use Apie\Core\Metadata\Strategy\ExceptionStrategy;
14use Apie\Core\Metadata\Strategy\ItemHashmapStrategy;
15use Apie\Core\Metadata\Strategy\ItemListObjectStrategy;
16use Apie\Core\Metadata\Strategy\PolymorphicEntityStrategy;
17use Apie\Core\Metadata\Strategy\RegularObjectStrategy;
18use Apie\Core\Metadata\Strategy\ScalarStrategy;
19use Apie\Core\Metadata\Strategy\UnionTypeStrategy;
20use Apie\Core\Metadata\Strategy\UploadedFileStrategy;
21use Apie\Core\Metadata\Strategy\ValueObjectStrategy;
22use LogicException;
23use ReflectionClass;
24use ReflectionIntersectionType;
25use ReflectionMethod;
26use ReflectionNamedType;
27use ReflectionType;
28use ReflectionUnionType;
29
30final class MetadataFactory
31{
32    private function __construct()
33    {
34    }
35
36    /**
37     * @param ReflectionClass<object> $class
38     */
39    public static function getMetadataStrategy(ReflectionClass $class): StrategyInterface
40    {
41        if (BuiltInPhpClassStrategy::supports($class)) {
42            return new BuiltInPhpClassStrategy($class);
43        }
44        if (ScalarStrategy::supports($class)) {
45            return new ScalarStrategy(ScalarType::STDCLASS);
46        }
47        if (EnumStrategy::supports($class)) {
48            return new EnumStrategy($class);
49        }
50        if (PolymorphicEntityStrategy::supports($class)) {
51            return new PolymorphicEntityStrategy($class);
52        }
53        if (CompositeValueObjectStrategy::supports($class)) {
54            return new CompositeValueObjectStrategy($class);
55        }
56        if (ItemListObjectStrategy::supports($class)) {
57            return new ItemListObjectStrategy($class);
58        }
59        if (ItemHashmapStrategy::supports($class)) {
60            return new ItemHashmapStrategy($class);
61        }
62        if (DtoStrategy::supports($class)) {
63            return new DtoStrategy($class);
64        }
65        if (ValueObjectStrategy::supports($class)) {
66            return new ValueObjectStrategy($class);
67        }
68        if (ExceptionStrategy::supports($class)) {
69            return new ExceptionStrategy($class);
70        }
71        if (UploadedFileStrategy::supports($class)) {
72            return new UploadedFileStrategy($class);
73        }
74        if (RegularObjectStrategy::supports($class)) {
75            return new RegularObjectStrategy($class);
76        }
77
78        throw new InvalidTypeException($class->name, 'Apie supported object');
79    }
80
81    public static function getScalarForType(?ReflectionType $typehint, bool $nullable = true): ScalarType
82    {
83        if ($typehint === null) {
84            return ScalarType::MIXED;
85        }
86        return self::getMetadataStrategyForType($typehint)
87            ->getResultMetadata(new ApieContext())
88            ->toScalarType($nullable);
89    }
90
91    public static function getMetadataStrategyForType(ReflectionType $typehint): StrategyInterface
92    {
93        if ($typehint instanceof ReflectionUnionType) {
94            $metadata = [];
95            foreach ($typehint->getTypes() as $type) {
96                $metadata[] = self::getMetadataStrategyForType($type)->getCreationMetadata(new ApieContext());
97            }
98            return new UnionTypeStrategy(...$metadata);
99        }
100        if ($typehint instanceof ReflectionIntersectionType) {
101            throw new LogicException('Intersection typehints are not supported yet');
102        }
103        assert($typehint instanceof ReflectionNamedType);
104        if ($typehint->isBuiltin()) {
105            if ($typehint->getName() === 'null') {
106                return new ScalarStrategy(ScalarType::NULLVALUE);
107            }
108            if ($typehint->getName() === 'mixed') {
109                return new ScalarStrategy(ScalarType::MIXED);
110            }
111            $strategy = new ScalarStrategy(
112                match ($typehint->getName()) {
113                    'string' => ScalarType::STRING,
114                    'float' => ScalarType::FLOAT,
115                    'int' => ScalarType::INTEGER,
116                    'array' => ScalarType::ARRAY,
117                    'mixed' => ScalarType::MIXED,
118                    'bool' => ScalarType::BOOLEAN,
119                    'true' => ScalarType::BOOLEAN,
120                    'false' => ScalarType::BOOLEAN,
121                    default => throw new InvalidTypeException($typehint->getName(), 'string|float|int|null|array|mixed|bool')
122                }
123            );
124        } else {
125            $strategy = self::getMetadataStrategy(new ReflectionClass($typehint->getName()));
126        }
127        if ($typehint->allowsNull()) {
128            return new UnionTypeStrategy($strategy, new ScalarMetadata(ScalarType::NULLVALUE));
129        }
130
131        return $strategy;
132    }
133
134    public static function getMethodMetadata(ReflectionMethod $method, ApieContext $context): MetadataInterface
135    {
136        $fields = [];
137        foreach ($method->getParameters() as $parameter) {
138            $fields[$parameter->name] = new ConstructorParameter($parameter);
139        }
140        return new CompositeMetadata(new MetadataFieldHashmap($fields));
141    }
142
143    /**
144     * @param ReflectionClass<object>|ReflectionType $typehint
145     */
146    public static function getCreationMetadata(ReflectionClass|ReflectionType $typehint, ApieContext $context): MetadataInterface
147    {
148        if ($typehint instanceof ReflectionType) {
149            return self::getMetadataStrategyForType($typehint)->getCreationMetadata($context);
150        }
151        return self::getMetadataStrategy($typehint)->getCreationMetadata($context);
152    }
153
154    /**
155     * @param ReflectionClass<object>|ReflectionType $typehint
156     */
157    public static function getModificationMetadata(ReflectionClass|ReflectionType $typehint, ApieContext $context): MetadataInterface
158    {
159        if ($typehint instanceof ReflectionType) {
160            return self::getMetadataStrategyForType($typehint)->getModificationMetadata($context);
161        }
162        return self::getMetadataStrategy($typehint)->getModificationMetadata($context);
163    }
164
165    /**
166     * @param ReflectionClass<object>|ReflectionType $typehint
167     */
168    public static function getResultMetadata(ReflectionClass|ReflectionType $typehint, ApieContext $context): MetadataInterface
169    {
170        if ($typehint instanceof ReflectionType) {
171            return self::getMetadataStrategyForType($typehint)->getResultMetadata($context);
172        }
173        return self::getMetadataStrategy($typehint)->getResultMetadata($context);
174    }
175}